﻿

using System;
using System.ComponentModel;

namespace Library.OrmEnterpriseEntity.TableInfo {
	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="T">Is .net type map to Db column type</typeparam>
	public abstract class Column<T> : DbColumn, IFormattable {
		protected Column(DbColumnView metadata, DbTable table)
			: base(metadata, table) {
			Init();
		}
		private void Init() {
			BeginInit();
			Value = Default;
			Events = new EventHandlerList();
			EndInit();
		}

		protected EventHandlerList Events { get; private set; }
		internal static readonly Type ColumnType = typeof(T);
		/// <summary>
		/// 
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		public static implicit operator T(Column<T> value) {
			if (value == null) return default(T);
			return value.Value;
		}


		protected sealed override object DefaultValue {
			get { return Default; }
		}

		protected virtual T Default {
			get { return default(T); }
		}
		internal override bool ValueNotDefault {
			get { return !Equals(Value, Default); }
		}
		public override Type PropertyType {
			get { return ColumnType; }
		}
		//public sealed override object GetValue() {
		//  return Value;
		//}
		private T _value;
		/// <summary>
		/// Get or set value of column
		/// </summary>
		public T Value {
			get { return _value; }
			set { SetValue(ref _value, value); }
		}

		protected virtual void SetValue(ref T oldValue, T newValue) {
			if (!Equal(oldValue, newValue)) {
				if (!IsInitialize) {
					var old = oldValue;
					OnValueChanging(new ColumnValueChangingEventArgs<T>(old, newValue));
					Table.OnPropertyChanging(ColumnName);
				}
				oldValue = newValue;

				switch (Table.State) {
					case EntityState.DbNoChange:
						Table.State = EntityState.DbChange;
						break;
				}
				if (!IsInitialize) {
					OnValueChanged(EventArgs.Empty);
					Table.OnPropertyChanged(ColumnName);
				}
			}
		}



		public override object DbValue {
			get { return Value; }
			set {
				Value = ConvertFrom(value);
			}
		}

		public override bool Validate() {
			ValidationEventHandler handler = Validation;

			if (handler != null) {
				var e = new ValidationEventArgs();
				handler(this, e);
				if (e.Handle) {
					return e.Result;
				}
				return base.Validate();

			}
			return base.Validate();

		}


		protected abstract T ConvertFrom(object value);

		protected abstract bool Equal(T t1, T t2);

		///<summary>
		///</summary>
		public event ValidationEventHandler Validation;
		public override bool Equals(object obj) {
			if (ReferenceEquals(this, obj)) return true;
			if (obj is T) {
				return Equals((T)obj);
			}
			if (obj is Column<T>) {
				return Equals((Column<T>)obj);
			}

			return false;
		}

		///<summary>
		///</summary>
		///<param name="value"></param>
		///<returns></returns>
		public bool Equals(T value) {
			return Equal(Value, value);
		}
		///<summary>
		///</summary>
		///<param name="column"></param>
		///<returns></returns>
		public bool Equals(Column<T> column) {
			return (column.Table.TableName == Table.TableName && column.ColumnName == ColumnName &&
							Equals(Value, column.Value));
		}


		public override int GetHashCode() {
			return _value.GetHashCode();
		}

		public virtual string ToString(string format, IFormatProvider formatProvider) {
			return _value.ToString();
		}


		private static readonly object EventValueChanged = new object();
		private static readonly object EventValueChanging = new object();

		protected virtual void OnValueChanging(ColumnValueChangingEventArgs<T> e) {
			if (IsInitialize) return;
			var handler = Events[EventValueChanging] as EventHandler<ColumnValueChangingEventArgs<T>>;
			if (handler != null) handler(this, e);
		}

		protected virtual void OnValueChanged(EventArgs e) {
			if (IsInitialize) return;
			var handler = Events[EventValueChanged] as EventHandler;
			if (handler != null) handler(this, e);
		}
		///<summary>
		/// Fire when Value changed
		///</summary>
		public event EventHandler ValueChanged {
			add {
				Events.AddHandler(EventValueChanged, value);
			}
			remove {
				Events.RemoveHandler(EventValueChanged, value);
			}
		}
		///<summary>
		/// Fire when Value changing
		///</summary>
		public event EventHandler<ColumnValueChangingEventArgs<T>> ValueChanging {
			add {
				Events.AddHandler(EventValueChanging, value);
			}
			remove {
				Events.RemoveHandler(EventValueChanging, value);
			}
		}

	}
}
